/*
 * Copyright 2007 Rob Nielsen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.proxymatic.core.engine;

import com.googlecode.proxymatic.core.ExceptionHandler;
import com.googlecode.proxymatic.core.MethodInvoker;
import com.googlecode.proxymatic.core.ProxyConfiguration;
import com.googlecode.proxymatic.core.util.PrimitiveUtil;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Iterator;

public class MethodExecutor implements Serializable {
    public Object invokeMethod(MethodInvoker handler, Method method, Object[] objects, ProxyConfiguration proxyConfiguration) throws Throwable {
        try {
            Object returnValue = handler.invoke(method, objects, proxyConfiguration.getRuntimeContext());
            if (!isReturnTypeValid(method.getReturnType(), returnValue)) {
                throw new IllegalStateException("Handler '" + handler + "' returned '" + returnValue + "' from '" + method + "'");
            }
            return returnValue;
        } catch (Throwable th) {
            for (Iterator iterator = proxyConfiguration.getExceptionHandlers().iterator(); iterator.hasNext();) {
                ExceptionHandler exceptionHandler = (ExceptionHandler) iterator.next();
                if (exceptionHandler.supportsException(th.getClass(), method)) {
                    return exceptionHandler.handleException(method, th);
                }
            }
            throw new IllegalStateException("Unhandled exception thrown. This should have been picked up by the ProxyBuilder!", th);
        }
    }

    boolean isReturnTypeValid(Class returnType, Object o) {
        if (returnType.equals(void.class)) {
            return o == null;
        }
        if (returnType.isPrimitive()) {
            return PrimitiveUtil.getBoxedClass(returnType).isInstance(o);
        }
        return o == null || returnType.isInstance(o);
    }
}
